Remove narrow-to-wide formatter specializations - #41143
Conversation
There was a problem hiding this comment.
Pull request overview
Removes the custom std::formatter narrow→wide specializations and makes UTF-8→UTF-16 conversions explicit at formatting boundaries (localization helpers, user-error paths, reporters, and tests), avoiding collisions with libc++’s deleted cross-encoding formatters.
Changes:
- Removed
char→wchar_tstd::formatterspecializations and updated thestd::source_locationwide formatter to explicitly convertfunction_name()/file_name(). - Updated generated localization helpers to materialize converted arguments before
make_wformat_args/vformat. - Updated Windows WSLC code paths and tests to explicitly call
wsl::shared::string::MultiByteToWidewhere wide formatting/user errors/reporting consume narrow values.
Reviewed changes
Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/generateLocalizationHeader.ps1 | Generates WIN32-only format-argument adapters that materialize narrow→wide conversions before vformat. |
| src/shared/inc/stringshared.h | Removes narrow→wide std::formatter specializations; updates std::source_location wide formatter to explicitly convert. |
| src/windows/wslcsession/WSLCSession.cpp | Converts Docker error strings to wide before THROW_HR_WITH_USER_ERROR* / warnings. |
| src/windows/wslcsession/WSLCContainer.cpp | Converts Docker error strings to wide before THROW_HR_WITH_USER_ERROR*. |
| src/windows/wslcsession/DockerHTTPClient.h | Converts Docker exception message to wide before user-error macro. |
| src/windows/wslc/services/ImageProgressCallback.cpp | Explicit narrow→wide conversions for reporter output and formatted status lines. |
| src/windows/wslc/services/BuildImageCallback.cpp | Explicit narrow→wide conversions for reporter output. |
| src/windows/wslc/services/ContainerModel.cpp | Makes user-error messages wide by converting narrow strings explicitly. |
| src/windows/wslc/services/ContainerService.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/services/SessionService.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/services/WinCredStorage.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/services/FileCredStorage.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/tasks/ImageTasks.cpp | Converts image IDs to wide before wide reporter output. |
| src/windows/wslc/tasks/NetworkTasks.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/tasks/RegistryTasks.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/tasks/VolumeTasks.cpp | Removes implicit formatting conversion by passing narrow args to localized helper directly. |
| src/windows/wslc/commands/VersionCommand.cpp | Converts package version to wide for wide reporter output. |
| src/windows/common/WSLCUserSettings.cpp | Removes implicit formatting conversion by passing e.what() directly to localized helper. |
| test/windows/Common.h | Updates unordered-vector mismatch formatting to explicitly convert std::string to wide for diagnostics. |
| test/windows/Common.cpp | Updates pattern-mismatch diagnostics to explicitly convert narrow strings to wide. |
| test/windows/WSLCTests.cpp | Converts narrow values to wide when formatting test diagnostics / expected strings. |
| test/windows/UnitTests.cpp | Converts narrow values to wide where wide formatting is used. |
| test/windows/SimpleTests.cpp | Converts narrow values to wide where wide formatting is used. |
| test/windows/DrvFsTests.cpp | Converts narrow paths to wide where wide formatting is used. |
| test/windows/wslc/e2e/WSLCE2EWarningTests.cpp | Removes extra conversion at call site now handled by localized helper / explicit conversions. |
| test/windows/wslc/e2e/WSLCE2EImageDeleteTests.cpp | Converts container/image IDs to wide when formatting expected stderr. |
| test/windows/wslc/e2e/WSLCE2EHelpers.cpp | Converts narrow error messages/IDs to wide for logging/expected output. |
| test/windows/wslc/e2e/WSLCE2EGlobalTests.cpp | Converts WSL_PACKAGE_VERSION to wide for formatted output. |
| test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp | Converts prompt to wide when formatting interactive command line. |
| test/windows/wslc/e2e/WSLCE2EContainerExecTests.cpp | Converts prompt/IDs to wide when formatting command lines and expected stderr. |
| test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp | Converts registry address/prompt to wide when formatting command lines. |
| test/windows/wslc/e2e/WSLCE2EContainerAttachTests.cpp | Converts prompt to wide when formatting command line. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
tools/generateLocalizationHeader.ps1:53
- FormatArgument(const std::basic_string<...>&) currently constructs a temporary std::string and then calls MultiByteToWide(const std::string&), which immediately calls c_str() and ignores the explicit size. This adds an unnecessary allocation/copy for non-std::string basic_string types without improving correctness.
src/windows/wslc/services/ContainerModel.cpp:29 - std::all_of(..., ::isdigit) is undefined behavior for chars with the high bit set because ::isdigit expects an unsigned char value (or EOF). This can mis-validate non-ASCII input depending on signedness/locale.
static auto parsePort = [](const std::string& value, const std::wstring& errorMessage) -> uint16_t {
try
{
// Ensure the value is not empty and contains only digits before parsing
if (value.empty() || !std::all_of(value.begin(), value.end(), ::isdigit))
{
dkbennett
left a comment
There was a problem hiding this comment.
Its unfortunate that we have to remove this specifier; I was relying on it in the Reporter class to keep the string use simple and naturally handle wide and narrow strings. I might update the signature of the Reporter in a followup to regain this functionality but this looks fine for me. Just a minor nit about the namespace clutter where MultiByteToWide was added.
Would be nice if we could align around wide strings wherever possible and maybe we wouldn't need so many MultiByteToWide calls in the client code.
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
032a2ce to
8930b16
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
tools/generateLocalizationHeader.ps1:31
- The localization FormatArgument() overload set doesn’t handle
const char[N]arguments. Because generated helpers takeconst T¶meters, passing a narrow string literal (or other char array) will deduceT = const char[N]and fall back to the genericFormatArgument(const T&), which then forwards the array intostd::make_wformat_argsand fails to compile (nochar[] -> wchar_tformatter). The PR description mentions supporting character arrays, so this appears to be an omission.
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
tools/generateLocalizationHeader.ps1:36
- On WIN32, FormatArgument currently handles char pointers and std::string, but not character arrays (e.g. string literals or const char[N]). Because the generated message helpers take arguments as
const T&, string literals bind asconst char(&)[N]and will fall through to the genericFormatArgument(const T&), reintroducing the deleted/removed cross-encoding formatting problem when building wide messages. This shows up in existing call sites likeLocalization::MessageConfigExpected("']'", ...)in src/shared/configfile/configfile.cpp.
Add an overload for const char (&)[N] (and optionally char (&)[N]) so arrays are converted via MultiByteToWide before make_wformat_args.
Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
Reverts commit a21d5f1. Copilot-Session: a404d085-142f-448f-b7fa-7ccc6528e844
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 32 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
tools/generateLocalizationHeader.ps1:61
localization_detail::FormatArgumentdoesn't currently adaptchar[N]/string literals on Windows. Because the generatedLocalization::MessageXxxtemplates take args asconst T&, a string literal deducesT = const char[N], and overload resolution prefers the genericFormatArgument(const T&)(exact match) overFormatArgument(const char*)(array-to-pointer conversion). That leaveschar[N]reachingstd::make_wformat_argsunconverted, which will fail to compile now that the narrow->widestd::formatter<char[N], wchar_t>specializations were removed.
src/windows/wslc/services/ContainerModel.cpp:133- These
THROW_HR_WITH_USER_ERRORcalls use hard-coded user-facing strings. For consistency with other validation errors in this codepath (which usewsl::shared::Localization::...), consider adding localized messages for these port-range validation failures and using them here.
if (m_containerPort.Count() == 0)
{
THROW_HR_WITH_USER_ERROR(E_INVALIDARG, L"Container port must specify at least one port.");
}
| THROW_HR_WITH_USER_ERROR( | ||
| E_INVALIDARG, "Invalid protocol specified in port mapping. Only 'tcp' and 'udp' are supported."); | ||
| E_INVALIDARG, L"Invalid protocol specified in port mapping. Only 'tcp' and 'udp' are supported."); | ||
| } |
Summary
Follow-up to #41071. Remove the custom narrow-to-wide
std::formatterspecializations entirely and make cross-encoding explicit at the appropriate boundary.Generated localization helpers now adapt
char*, character arrays,std::basic_string<char>, andstd::basic_string_view<char>arguments throughwsl::shared::string::MultiByteToWide. Adapted values are materialized as local variables beforemake_wformat_argsso their storage remains valid throughvformat.Direct wide
std::format, Reporter, and user-error paths convert narrow values explicitly.Validation
WSLCTests::InvalidNamesWSLCVolumeMountUnitTests::VolumeMount_IsValidNamedVolumeName_InvalidNamesUnitTests::WslInfo